package san.java.day15;


import java.util.Random;

/**
 * 线程的同步与死锁:
 *  解决方法 --> 临界区: 一个时刻只有一个线程进入临界区
 *      在java中只需要在方法前加上 synchronized 关键字即可
 *
 *
 *      注意:
 *          1. 在普通方法上加synchronized, 锁的是整个对象,不是一个方法,不同的对象就是不同的锁
 *          2. 静态方法加synchronized不锁对象
 *          3. synchronized(Object a) {}  锁对象obj中的代码块, 不锁对象, 也不锁其他对象的代码块
 *
 *      总结:
 *          1. 如果针对对象要加同步锁,那么将锁加在普通方法上
 *          2. 如果针对某一段代码需要加同步锁,那么直接在代码上加同步锁
 */

class Account {

    private long money;

    private static int test = 1000;
    private static int test1 = 1000;

    public Account(long money) {
        this.money = money;
    }

    /**
     * 取钱
     * @param money
     */
    public synchronized void drawing (long money) throws RuntimeException {

        if (money < 0) {
            throw new RuntimeException("输入金额无效");
        }

        if (this.money < money) {
            throw new RuntimeException("余额不足");
        }
        this.money -= money;
    }

    public void drawing1 (long money) throws RuntimeException {

        if (money < 0) {
            throw new RuntimeException("输入金额无效");
        }

        if (this.money < money) {
            throw new RuntimeException("余额不足");
        }
        this.money -= money;
    }

    public void drawing2 (long money) throws RuntimeException {

        if (money < 0) {
            throw new RuntimeException("输入金额无效");
        }

        if (this.money < money) {
            throw new RuntimeException("余额不足");
        }

        synchronized (this) {
            String name = Thread.currentThread().getName();
            if (name.equalsIgnoreCase("weChat")) {
                try {
                    this.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            System.out.println(name + "在操作");
            this.money -= money;

            if (name.equalsIgnoreCase("zfb")) {
                try {
                    this.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        test--;
    }

    public void drawing3 (long money, Account a) throws RuntimeException {

        if (money < 0) {
            throw new RuntimeException("输入金额无效");
        }

        if (this.money < money) {
            throw new RuntimeException("余额不足");
        }

        synchronized (a) {
            this.money -= money;
        }
        test--;
    }

    public synchronized static void testSync() {
        test--;
    }

    public static void testSync1() {
        test--;
    }

    public synchronized static void testSync3() {
        test1--;
    }

    public int getTest() {
        return test;
    }

    public long getMoney() {
        return this.money;
    }
}

class User implements Runnable {

    Account account;
    Account account1;
    long money;
    Random r = new Random();

    public User(Account account, long money) {
        this.account = account;
        this.money = money;
    }

    @Override
    public void run() {
//        if (r.nextBoolean()) {
//            account.drawing(this.money);
//        } else {
//            account.drawing1(this.money);
//        }

//        if (r.nextBoolean()) {
//            Account.testSync();
//        } else {
//            Account.testSync1();
//        }
//
////        Account.testSync();
//        account.drawing1(this.money);

        account.drawing2(this.money);
    }
}

public class Thread_07 {

    public static void main(String[] args) {
        Account account = new Account(100000);
        Account account1 = new Account(100000);

//        for (int i = 0; i < 1000; i++) {
//            Thread thread = new Thread(new User(account, 100));
//            thread.start();
//            try {
//                thread.join();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(account.getMoney());
//        }

        for (int i = 0; i < 1000; i++) {
            new Thread(new User(account, 100), "zfb").start();
            new Thread(new User(account1, 100), "weChat").start();
            System.out.println(account.getTest());
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test: " + account.getTest());
        System.out.println("money: " + account.getMoney());
    }
}
